#include "EstrategiaAlmacenamientoRegistro.h"
#include "../recursoDeAlmacenamiento/RecursoDeAlmacenamiento.h"
#include "../componentes/Componente.h"
#include "../componentes/Registro.h"
#include "IteradorRegistrosFijos.h"
#include "../componentes/RegistroFijo.h"
#include "../Bytes.h"


EstrategiaAlmacenamientoRegistro::EstrategiaAlmacenamientoRegistro(cantBytes tamanioRegistro) {

	this->recurso = NULL;
	this->tamanioRegistro = tamanioRegistro;
	this->tipoArchivo = ARCH_REGISTROS_TAM_FIJO;

	Bytes byteAux = crearBytesNulos(tamanioRegistro);
	RegistroFijo registro(byteAux);

	this->tamanioRegistroSerializado = registro.getTamanioSerializado();
	this->tamanioHeaderConPadding = this->tamanioRegistroSerializado;
	this->tamanioHeader = LONGITUD_HEADER_REGISTROS_FIJOS;
}

EstrategiaAlmacenamientoRegistro::~EstrategiaAlmacenamientoRegistro() {

}

bool EstrategiaAlmacenamientoRegistro::guardar(const Bytes& dato,long& numeroComponente) {

	if (dato.getTamanio() != this->tamanioRegistro) {
		cerr << "Error: El tamanio del dato no coincide con el tamanio especificado" << endl;
		return false;
	}

	if (numeroComponente == -1) {
		RegistroFijo registro(dato);
		Bytes serializacionRegistro = registro.serializarse();
		offset offsetFinal = this->recurso->getTamanioArchivo();

		this->recurso->escribir(serializacionRegistro,offsetFinal);
		numeroComponente = (offsetFinal - this->tamanioHeaderConPadding) / this->tamanioRegistroSerializado;
	} else {
		offset offsetDato = numeroComponente*this->tamanioRegistroSerializado + this->tamanioHeaderConPadding;

		if (offsetDato > (this->recurso->getTamanioArchivo())) {
			cerr << "Error: Numero de componente Invalido." << endl;
			return false;
		}

		RegistroFijo registro(dato);
		Bytes serializacionRegistro = registro.serializarse();

		this->recurso->escribir(serializacionRegistro,offsetDato);
	}

	return true;
}

void EstrategiaAlmacenamientoRegistro::completarHeader(Bytes& header) {
	cantBytes tamanioRegistro = this->tamanioRegistro;
	header.agregarAlFinal((void*) &tamanioRegistro, sizeof(tamanioRegistro));
	header.agregarAlFinal(crearBytesNulos(this->tamanioHeaderConPadding - this->tamanioHeader));
}

Iterador* EstrategiaAlmacenamientoRegistro::getIterador() {

	return new IteradorRegistrosFijos(this);
}

bool EstrategiaAlmacenamientoRegistro::headerCorrupto() {

	if (recurso == NULL) {
		return true;
	}

	Bytes header = recurso->leer(0, this->tamanioHeaderConPadding);

	if (header.getTamanio() != this->tamanioHeaderConPadding) {
		return true;
	}

	if (header.getSubBytes(0, 1).toString() != this->tipoArchivo) {
		return true;
	}

	if (bytesToCantBytes(header.getSubBytes(1, 4)) != this->tamanioRegistro) {
		return true;
	}

	return false;
}

Bytes EstrategiaAlmacenamientoRegistro::obtener(offset numeroComponente, Key clave, TipoClave* extractor) {

	Bytes datoLeido;

	offset offsetDato = (numeroComponente)*this->tamanioRegistroSerializado + this->tamanioHeaderConPadding;

	if (offsetDato > (this->recurso->getTamanioArchivo() - this->tamanioRegistroSerializado)) {
		cerr << "Error: Numero de componente Invalido." << endl;
		return Bytes("");
	}

	Bytes bytesRegistro = this->recurso->leer(offsetDato,this->tamanioRegistroSerializado);

	if (bytesRegistro.getTamanio() == 0) {
		return Bytes("");
	}

	RegistroFijo registro(bytesRegistro);
	registro.hidratarse(bytesRegistro);

	if (registro.estaVivo()) {
		return registro.getDato();
	} else {
		return Bytes("");
	}
}

bool EstrategiaAlmacenamientoRegistro::eliminar(offset numeroComponente, Key clave, TipoClave* extractor) {

	Bytes datoLeido;

	offset offsetDato = (numeroComponente)*this->tamanioRegistroSerializado + this->tamanioHeaderConPadding;

	if (offsetDato > (this->recurso->getTamanioArchivo() - this->tamanioRegistroSerializado)) {
		cerr << "Error: Ah intentado eliminar un componente no valido." << endl;
		return false;
	}

	Bytes bytesRegistro = this->recurso->leer(offsetDato,this->tamanioRegistroSerializado);

	if (bytesRegistro.getTamanio() == 0) {
		return false;
	}

	RegistroFijo registro(bytesRegistro);
	registro.hidratarse(bytesRegistro);

	if (!registro.estaVivo()) {
		return false;
	}

	registro.matar();

	Bytes bytesRegistroMuerto = registro.serializarse();

	this->recurso->escribir(bytesRegistroMuerto,offsetDato);

	return true;
}

unsigned EstrategiaAlmacenamientoRegistro::getCantRegistros() {
	return (this->recurso->getTamanioArchivo() - this->tamanioHeaderConPadding) / this->tamanioRegistroSerializado;
}
